File: /var/www/html/dashboard.orbiwheels.com/vendor/cuyz/valinor/src/Cache/TypeFilesWatcher.php
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Cache;
use Closure;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use CuyZ\Valinor\Library\Settings;
use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Utility\Reflection\Reflection;
use CuyZ\Valinor\Utility\TypeHelper;
use ReflectionFunction;
use function array_filter;
use function array_unique;
use function array_values;
/** @internal */
final class TypeFilesWatcher
{
public function __construct(
private Settings $settings,
private ClassDefinitionRepository $classDefinitionRepository,
) {}
/**
* This method returns a list of files in which are declared all types that
* compose the given type. If the given type is composed of other types,
* they are recursively resolved as well. If the type is a class, all
* properties are also resolved.
*
* In addition, all callable that were given to the global settings are
* checked, and their file names are added to the list.
*
* This file list can then be used by the `FileWatchingCache` to detect
* changes to these files and invalidate the cache entries if needed.
*
* Example of returned value:
*
* ```php
* [
* // An entity representing a user
* '/root/path/src/Domain/User/User.php',
*
* // The user has an `$email` property and a `$phoneNumber` property
* '/root/path/src/Domain/Shared/Email.php',
* '/root/path/src/Domain/Shared/PhoneNumber.php',
*
* // The settings contain a custom constructor defined in a file
* '/root/path/src/Infrastructure/Mapper/CustomConstructor.php',
*
* // The settings contain a custom exception filter
* '/root/path/src/Infrastructure/Mapper/CustomExceptionFilter.php',
*
* // And maybe moreā¦
* ];
* ```
*
* @return list<non-empty-string>
*/
public function for(Type $type): array
{
// Merging the files bound to settings and the files bound to the type
$files = [
...array_map(
fn (callable $callable) => (new ReflectionFunction(Closure::fromCallable($callable)))->getFileName(),
$this->settings->callables(),
),
...$this->filesToWatch($type),
];
// Removing the duplicates
$files = array_unique($files);
// Filtering the empty/invalid file names
$files = array_filter($files, fn ($value) => is_string($value));
/** @var list<non-empty-string> */
return array_values($files);
}
/**
* @param array<non-empty-string> $files
* @return array<non-empty-string>
*/
private function filesToWatch(Type $type, array $files = []): array
{
if (isset($files[$type->toString()])) {
// Prevents infinite loop in case of circular references
return [];
}
foreach (TypeHelper::traverseRecursively($type) as $subType) {
$files = [...$files, ...$this->filesToWatch($subType, $files)];
}
if ($type instanceof ObjectType) {
$fileName = Reflection::class($type->className())->getFileName();
if (! $fileName) {
return [];
}
$files[$type->toString()] = $fileName;
$class = $this->classDefinitionRepository->for($type);
foreach ($class->properties as $property) {
$files = [...$files, ...$this->filesToWatch($property->type, $files)];
}
}
return $files;
}
}